#!/usr/bin/env fq -d csv -r -f
#
# cat tls-parameters-4.csv other.csv | ./ciphersuites.jq  | gofmt > ciphersuits.go
#
# csv from:
# https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml
#
# Example line:
# "0xC0,0xA0",TLS_RSA_WITH_AES_128_CCM_8,Y,N,[RFC6655]
#

def from_suitname:
  ( . as $name
  | ( capture(`^(?<proto>[A-Z0-9]+)_(?<ka>\w+)_WITH_(?<cipher>\w+)_(?<hash>NULL|SHA.*|MD.*|GOSTR3411|IMIT)$`)
    //
      ( capture(`^(?<proto>[A-Z0-9]+)`)
      | .ka = "UNKNOWN"
      | .cipher = "UNKNOWN"
      | .hash = "UNKNOWN"
      | .ka = "UNKNOWN"
      )
    )
  # special case to get valid cipher const name
  | with_entries(.value |= if test("^\\d") then "_"+. end)
  | .name = $name
  );

def from_params:
  ( .[1:]
  | map(
    ( select(.[0])
    | select(.[1] | test("unassigned|reserved";"i") | not))
    | { number: (.[0] | split(",") | map(.[2:]) | join("") | "0x" + ascii_downcase)
      }
    + (.[1] | from_suitname)
    )
  );

def prefix_special($name):
  if . == "NULL" or . == "UNKNOWN" then "\(.)_\($name)" end;

def prefix_specials($name):
  .[] |= prefix_special($name);

def enum($name):
  ( "type \($name) int"
  , ""
  , "const ("
  , (.[0] | "  \(.) \($name) = iota")
  , (.[1:][] | "  \(.)")
  , ")"
  );

( from_params as $params
| ($params | map(.proto) | unique | prefix_specials("Protocol")) as $protos
| ($params | map(.ka) | unique | prefix_specials("KeyAgreement")) as $kas
| ($params | map(.cipher) | unique | prefix_specials("Cipher")) as $ciphers
| ($params | map(.hash) | unique | prefix_specials("Hash")) as $hashs
| "// Package ciphersuites defines TLS and SSL cipher suit properties"
, "// generated by ciphersuites.jq"
, "package ciphersuites"
, ($protos | enum("Protocol"))
, ($kas | enum("KeyAgreement"))
, ($ciphers | enum("Cipher"))
, ($hashs | enum("Hash"))
, ( "const ("
  , ($params[] | "  \(.name) = \(.number)")
  , ")"
  )
, "type Suit struct {"
, "  Name         string"
, "  Protocol     Protocol"
, "  KeyAgreement KeyAgreement"
, "  Cipher       Cipher"
, "  Hash         Hash"
, "}"
, ( "var Suits = map[int]Suit{"
  , ( $params[] | "\(.name): {Name: \"\(.name)\", Protocol: \(.proto | prefix_special("Protocol")), KeyAgreement: \(.ka | prefix_special("KeyAgreement")), Cipher: \(.cipher | prefix_special("Cipher")), Hash: \(.hash | prefix_special("Hash"))},")
  , "}"
  )
)

